package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SearchStats;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.aggregations.Buckets;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.model.album.*;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ReactiveDocumentOperations;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;


    @Autowired
    private RedissonClient redissonClient;

    /**
     * 将指定专辑封装专辑索引库文档对象，完成文档信息
     *
     * @param albumId
     */
    @Override
    public void upperAlbum(Long albumId) {

        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
        //1.远程调用专辑服务，获取专辑信息（包含专辑标签列表），属性拷贝到索引库文档对象
        //1.1 远程调用专辑服务-专辑信息  分析：该任务不需要获取其他任务结果，当前任务需要结果给其他任务调用
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfo, "专辑{}信息为空", albumId);

            //1.2 封装专辑文档对象专辑信息
            BeanUtil.copyProperties(albumInfo, albumInfoIndex);

            //1.3 封装专辑文档对象专辑标签列表
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)) {
                List<AttributeValueIndex> attributeValueIndexList
                        = albumAttributeValueVoList
                        .stream()
                        .map(albumAttributeValue -> BeanUtil.copyProperties(albumAttributeValue, AttributeValueIndex.class))
                        .collect(Collectors.toList());
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            }
            return albumInfo;
        }, threadPoolExecutor);

        //2.远程调用专辑服务,获取分类信息，封装三级分类ID 分析：该任务需要依赖专辑信息异步任务结果，当前任务无返回结果
        CompletableFuture<Void> categoryCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            BaseCategoryView categoryView = albumFeignClient.getCategoryView(albumInfo.getCategory3Id()).getData();
            Assert.notNull(categoryView, "分类{}信息为空", albumInfo.getCategory3Id());
            albumInfoIndex.setCategory1Id(categoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(categoryView.getCategory2Id());
        }, threadPoolExecutor);

        //3.远程调用用户服务，获取主播信息，封装主播名称 分析：该任务需要依赖专辑信息异步任务结果，当前任务无返回结果
        CompletableFuture<Void> userInfoCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(albumInfo.getUserId()).getData();
            Assert.notNull(userInfoVo, "主播{}信息为空", albumInfo.getUserId());
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
        }, threadPoolExecutor);


        //4.TODO 为了方便进行检索测试，随机产生专辑统计数值 封装专辑统计信息 将来改为远程调用
        CompletableFuture<Void> statCompletableFuture = CompletableFuture.runAsync(() -> {
            //4.1 随机产生四个数值作为统计值
            int num1 = RandomUtil.randomInt(500, 2000);
            int num2 = RandomUtil.randomInt(500, 1500);
            int num3 = RandomUtil.randomInt(500, 1000);
            int num4 = RandomUtil.randomInt(500, 1000);
            albumInfoIndex.setPlayStatNum(num1);
            albumInfoIndex.setSubscribeStatNum(num2);
            albumInfoIndex.setBuyStatNum(num3);
            albumInfoIndex.setCommentStatNum(num4);

            //4.2 基于统计值计算当前文档热度 统计量*动态权重
            BigDecimal bigDecimal1 = new BigDecimal("0.1").multiply(new BigDecimal(num1));
            BigDecimal bigDecimal2 = new BigDecimal("0.2").multiply(new BigDecimal(num2));
            BigDecimal bigDecimal3 = new BigDecimal("0.3").multiply(new BigDecimal(num3));
            BigDecimal bigDecimal4 = new BigDecimal("0.4").multiply(new BigDecimal(num4));
            BigDecimal hotScore = bigDecimal1.add(bigDecimal2).add(bigDecimal3).add(bigDecimal4);
            albumInfoIndex.setHotScore(hotScore.doubleValue());
        }, threadPoolExecutor);


        //组合四个异步任务
        CompletableFuture.allOf(
                albumInfoCompletableFuture,
                statCompletableFuture,
                categoryCompletableFuture,
                userInfoCompletableFuture
        ).join();

        //5.调用文档持久层接口保存专辑
        albumInfoIndexRepository.save(albumInfoIndex);

        //6.将专辑标题存入提词索引库
        this.saveSuggestIndex(albumInfoIndex);

        //7.将发布后专辑ID存入布隆过滤器
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        bloomFilter.add(albumId);
    }


    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    /**
     * 将专辑标题封装为提词索引库文档对象，存入ES索引库
     *
     * @param albumInfoIndex
     */
    private void saveSuggestIndex(AlbumInfoIndex albumInfoIndex) {
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(albumInfoIndex.getId().toString());
        //提词文档中 原始文本内容
        suggestIndex.setTitle(albumInfoIndex.getAlbumTitle());
        //用于自动补全字段 类型：Completion 存入汉字 ["明朝败家子"]
        suggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        //用于自动补全字段 类型：Completion 存入汉字全拼 ["mingchaobaijiazi"]
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinyinUtil.getPinyin(albumInfoIndex.getAlbumTitle(), "")}));
        //用于自动补全字段 类型：Completion 存入汉字拼音首字母 ["mcbjz"]
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinyinUtil.getFirstLetter(albumInfoIndex.getAlbumTitle(), "")}));
        suggestIndexRepository.save(suggestIndex);
    }

    /**
     * 将指定专辑从索引库删除
     *
     * @param albumId
     */
    @Override
    public void lowerAlbum(Long albumId) {
        albumInfoIndexRepository.deleteById(albumId.toString());
    }

    @Autowired
    private ElasticsearchClient elasticsearchClient;


    /**
     * 专辑站内检索
     *
     * @param albumIndexQuery 查询条件：关键字，分类，标签  分页参数 排序字段
     * @return
     */
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        try {
            //一、创建检索请求对象
            SearchRequest searchRequest = this.buildDSL(albumIndexQuery);
            System.err.println("本次检索DSL:");
            System.err.println(searchRequest.toString());
            //二、调用执行检索
            SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
            //三、解析ES响应结果
            return this.parseResult(searchResponse, albumIndexQuery);
        } catch (IOException e) {
            log.error("[搜索服务]站内检索异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    private static final String INDEX_NAME = "albuminfo";

    /**
     * 基于入参查询条件构建完整检索请求对象
     *
     * @param albumIndexQuery 查询条件
     * @return 检索请求对象
     */
    @Override
    public SearchRequest buildDSL(AlbumIndexQuery albumIndexQuery) {
        //1.创建检索请求构建器对象-封装检索索引库
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index(INDEX_NAME);
        //2.设置查询条件对应请求体参数中"query" 封装关键字、分类过滤、标签过滤
        String keyword = albumIndexQuery.getKeyword();
        //2.1 创建最外层组合多条件查询对象-封装所有查询条件
        BoolQuery.Builder allBoolQueryBuilder = new BoolQuery.Builder();
        //2.2 处理关键字检索 关键字全文检索专辑标题，简介。等值精确查询作者名称
        if (StringUtils.isNotBlank(keyword)) {
            allBoolQueryBuilder.must(
                    m -> m.bool(
                            b -> b.should(s -> s.match(ma -> ma.field("albumTitle").query(keyword)))
                                    .should(s -> s.match(ma -> ma.field("albumIntro").query(keyword)))
                                    .should(s -> s.term(t -> t.field("announcerName").value(keyword)))
                    )
            );
        }
        //2.3 处理分类过滤
        //2.3.1 判断1级分类是否提交，设置1级分类过滤
        if (albumIndexQuery.getCategory1Id() != null) {
            allBoolQueryBuilder.filter(f -> f.term(t -> t.field("category1Id").value(albumIndexQuery.getCategory1Id())));
        }
        //2.3.2 判断2级分类是否提交，设置2级分类过滤
        if (albumIndexQuery.getCategory2Id() != null) {
            allBoolQueryBuilder.filter(f -> f.term(t -> t.field("category2Id").value(albumIndexQuery.getCategory2Id())));
        }
        //2.3.3 判断3级分类是否提交，设置3级分类过滤
        if (albumIndexQuery.getCategory3Id() != null) {
            allBoolQueryBuilder.filter(f -> f.term(t -> t.field("category3Id").value(albumIndexQuery.getCategory3Id())));
        }
        //2.4 处理标签过滤
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (CollectionUtil.isNotEmpty(attributeList)) {
            //2.4.1 前端可能提交多个标签过滤条件 提交标签形式  标签id：标签值Id
            for (String s : attributeList) {
                //每循环一次，封装标签Nested查询
                String[] split = s.split(":");
                allBoolQueryBuilder.filter(f -> f.nested(n ->
                        n.path("attributeValueIndexList")
                                .query(q -> q.bool(
                                        b -> b.must(m -> m.term(t -> t.field("attributeValueIndexList.attributeId").value(split[0])))
                                                .must(m -> m.term(t -> t.field("attributeValueIndexList.valueId").value(split[1])))
                                ))
                ));
            }
        }
        builder.query(allBoolQueryBuilder.build()._toQuery());
        //3.设置分页对应请求体参数中"from" "size" 封装起始位置，页大小
        Integer pageSize = albumIndexQuery.getPageSize();
        int from = (albumIndexQuery.getPageNo() - 1) * pageSize;
        builder.from(from);
        builder.size(pageSize);
        //4.设置排序对应请求体参数中"sort" 前段提交参数=排序字段:排序方式
        String order = albumIndexQuery.getOrder();
        if (StringUtils.isNotBlank(order)) {
            //4.1 对排序字符按照:进行切割
            String[] split = order.split(":");
            //4.2 得到排序字段及排序方式 综合排序[1:desc] 播放量[2:desc] 发布时间[3:desc]
            String orderFiled = "";
            switch (split[0]) {
                case "1":
                    orderFiled = "hotScore";
                    break;
                case "2":
                    orderFiled = "playStatNum";
                    break;
                case "3":
                    orderFiled = "createTime";
                    break;
            }
            String finalOrderFiled = orderFiled;
            builder.sort(s -> s.field(f -> f.field(finalOrderFiled).order("asc".equals(split[1]) ? SortOrder.Asc : SortOrder.Desc)));
        }
        //5.设置高亮显示对应请求体参数中"highlight"
        if (StringUtils.isNotBlank(keyword)) {
            builder.highlight(h -> h.fields("albumTitle", f -> f.preTags("<font style='color:red'>").postTags("</font>")));
        }
        //6.设置查询及返回字段列表
        builder.source(s -> s.filter(f -> f.excludes("category1Id", "category2Id", "category3Id", "hotScore", "attributeValueIndexList.attributeId", "attributeValueIndexList.valueId")));
        //7.基于构建器对象得到检索请求对象
        return builder.build();
    }


    /**
     * 解析ES响应结果，将ES响应JSON转为AlbumSearchResponseVo对象
     *
     * @param searchResponse  ES响应结果
     * @param albumIndexQuery
     * @return
     */
    @Override
    public AlbumSearchResponseVo parseResult(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery albumIndexQuery) {
        AlbumSearchResponseVo vo = new AlbumSearchResponseVo();
        //1.封装分页相关信息 页码、页大小、总记录数、总页数
        //1.1 从入参中获取页码、页大小
        vo.setPageNo(albumIndexQuery.getPageNo());
        Integer pageSize = albumIndexQuery.getPageSize();
        vo.setPageSize(pageSize);
        //1.2 从ES检索结果中获取命中记录数
        long total = searchResponse.hits().total().value();
        //1.3 计算总页数
        long totalPages = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
        vo.setTotalPages(totalPages);

        //2.封装检索到专辑列表数据（处理高亮）
        //2.1 获取命中hits集合，遍历得到Hit对象中source(AlbumInfoIndex) 转为 AlbumInfoIndexVo
        List<Hit<AlbumInfoIndex>> hits = searchResponse.hits().hits();
        if (CollectionUtil.isNotEmpty(hits)) {
            List<AlbumInfoIndexVo> albumInfoIndexVoList = hits.stream().map(hit -> {
                //2.1.1 将得到AlbumInfoIndex类型转为AlbumInfoIndexVo
                AlbumInfoIndex albumInfoIndex = hit.source();
                AlbumInfoIndexVo albumInfoIndexVo = BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class);
                //2.1.2 处理高亮片段
                Map<String, List<String>> highlightMap = hit.highlight();
                if (CollectionUtil.isNotEmpty(highlightMap)) {
                    String albumTitleHighlight = highlightMap.get("albumTitle").get(0);
                    albumInfoIndexVo.setAlbumTitle(albumTitleHighlight);
                }
                return albumInfoIndexVo;
            }).collect(Collectors.toList());
            vo.setList(albumInfoIndexVoList);
        }
        return vo;
    }

    /**
     * 查询指定1级分类下置顶7个三级分类下热门专辑前6
     *
     * @param category1Id 一级分类ID
     * @return [{"baseCategory3":"分类信息","list":[{热门专辑}]}]
     */
    @Override
    public List<Map<String, Object>> getTopCategoryAlbumList(Long category1Id) {
        try {
            //1.远程调用专辑服务获取置顶三级分类列表（7个）
            List<BaseCategory3> baseCategory3List = albumFeignClient.getTopBaseCategory3(category1Id).getData();
            //1.1 遍历分类集合得到三级分类ID
            List<Long> category3IdList = baseCategory3List.stream().map(BaseCategory3::getId).collect(Collectors.toList());
            //1.1.1 检索需要将三级分类ID集合泛型转为FieldValue
            List<FieldValue> fieldValueList =
                    category3IdList.stream().map(category3Id -> FieldValue.of(category3Id)).collect(Collectors.toList());

            //1.2 后续封装响应结果中分类信息 将分类集合转为Map key:三级分类ID val:三级分类对象
            Map<Long, BaseCategory3> category3Map = baseCategory3List.stream()
                    .collect(Collectors.toMap(BaseCategory3::getId, baseCategory3 -> baseCategory3));

            //2.调用原生ES客户端进行检索
            SearchResponse<AlbumInfoIndex> searchResponse =
                    elasticsearchClient.search(
                            s -> s.index(INDEX_NAME).size(0)
                                    .query(q -> q.terms(t -> t.field("category3Id").terms(t1 -> t1.value(fieldValueList))))
                                    .aggregations(
                                            "category3Agg", a -> a.terms(t -> t.field("category3Id").size(10))
                                                    .aggregations("top6", a1 -> a1.topHits(t -> t.size(6).sort(sort -> sort.field(f -> f.field("hotScore").order(SortOrder.Desc)))))
                                    )
                            , AlbumInfoIndex.class);

            //3.解析ES聚合结果
            Map<String, Aggregate> aggregations = searchResponse.aggregations();
            if (CollectionUtil.isNotEmpty(aggregations)) {
                //3.1 获取到三级分类ID聚合桶集合
                Buckets<LongTermsBucket> category3AggBuckets = aggregations.get("category3Agg").lterms().buckets();
                //3.2 遍历三级分类ID聚合桶，每遍历一个得到某个置顶三级分类下热门专辑
                if (CollectionUtil.isNotEmpty(category3AggBuckets.array())) {
                    List<Map<String, Object>> list = category3AggBuckets.array().stream().map(category3Bucket -> {
                        //3.2.1 获取三级分类ID
                        long category3Id = category3Bucket.key();
                        BaseCategory3 baseCategory3 = category3Map.get(category3Id);
                        //3.3 基于当前分类桶继续下钻获取子聚合
                        List<AlbumInfoIndex> hotAlbumList = category3Bucket.aggregations().get("top6").topHits().hits().hits().stream()
                                .map(hotAlbumHit -> {
                                    String hotAlbumHitJsonStr = hotAlbumHit.source().toString();
                                    AlbumInfoIndex albumInfoIndex = JSON.parseObject(hotAlbumHitJsonStr, AlbumInfoIndex.class);
                                    return albumInfoIndex;
                                }).collect(Collectors.toList());
                        //3.4 构建置顶三级分类热门专辑Map对象
                        Map<String, Object> map = new HashMap<>();
                        map.put("baseCategory3", baseCategory3);
                        map.put("list", hotAlbumList);
                        return map;
                    }).collect(Collectors.toList());
                    return list;
                }
            }
            return null;
        } catch (IOException e) {
            log.error("[搜索服务]首页置顶分类热门专辑异常：{}", e);
            throw new RuntimeException(e);
        }
    }


    private static final String SUGGEST_INDEX = "suggestinfo";

    /**
     * 根据用户正在录入文本进行自动联想
     *
     * @param keyword 任意字符
     * @return 用于自动补全文本列表
     */
    @Override
    public List<String> completeSuggest(String keyword) {
        try {
            //1.发起建议词自动补全请求-采用Lambda表达式构建
            SearchResponse<SuggestIndex> searchResponse = elasticsearchClient
                    .search(
                            s -> s.index(SUGGEST_INDEX)
                                    .suggest(
                                            s1 -> s1.suggesters("suggestKeyword", f -> f.prefix(keyword).completion(c -> c.field("keyword").skipDuplicates(true).size(10)))
                                                    .suggesters("suggestPinyin", f -> f.prefix(keyword).completion(c -> c.field("keywordPinyin").skipDuplicates(true).size(10)))
                                                    .suggesters("suggestLetter", f -> f.prefix(keyword).completion(c -> c.field("keywordSequence").skipDuplicates(true).size(10)))
                                    )
                            , SuggestIndex.class);
            //2.解析建议词结果，注意封装 自动补全文本列表长度最多显示10条
            //2.1 根据三个自定义建议词解析ES建议结果，将结果放入集合中（去重）
            HashSet<String> suggestSet = new HashSet<>();
            suggestSet.addAll(this.parseSuggestResult("suggestKeyword", searchResponse));
            suggestSet.addAll(this.parseSuggestResult("suggestPinyin", searchResponse));
            suggestSet.addAll(this.parseSuggestResult("suggestLetter", searchResponse));
            //2.2 判断结果长度如果小于10，采用全文查询-查询专辑索引库 补全到10个
            if (suggestSet.size() < 10) {
                SearchResponse<AlbumInfoIndex> response = elasticsearchClient.search(
                        s -> s.index(INDEX_NAME).query(q -> q.match(m -> m.field("albumTitle").query(keyword))),
                        AlbumInfoIndex.class);
                List<Hit<AlbumInfoIndex>> hits = response.hits().hits();
                if (CollectionUtil.isNotEmpty(hits)) {
                    for (Hit<AlbumInfoIndex> hit : hits) {
                        String albumTitle = hit.source().getAlbumTitle();
                        suggestSet.add(albumTitle);
                        if (suggestSet.size() >= 10) {
                            break;
                        }
                    }
                }
            }
            List<String> list = new ArrayList<>(suggestSet);
            if (suggestSet.size() >= 10) {
                return new ArrayList<>(suggestSet).subList(0, 10);
            }
            return list;
        } catch (IOException e) {
            log.error("[搜索服务]关键词自动补全异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析自定义建议词结果
     *
     * @param suggestName    自定义建议词参数名称
     * @param searchResponse ES响应结果
     * @return
     */
    @Override
    public Collection<String> parseSuggestResult(String suggestName, SearchResponse<SuggestIndex> searchResponse) {
        List<String> list = new ArrayList<>();
        List<Suggestion<SuggestIndex>> suggestions = searchResponse.suggest().get(suggestName);
        if (CollectionUtil.isNotEmpty(suggestions)) {
            for (Suggestion<SuggestIndex> suggestion : suggestions) {
                List<CompletionSuggestOption<SuggestIndex>> options = suggestion.completion().options();
                for (CompletionSuggestOption<SuggestIndex> option : options) {
                    //得到提词索引库文档对象
                    SuggestIndex suggestIndex = option.source();
                    //将提词文档中原始内容
                    list.add(suggestIndex.getTitle());
                }
            }
        }
        return list;
    }

    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 更新Redis所有1级分类不同排序TOP10专辑
     */
    @Override
    public void updateLatelyAlbumRanking() {
        try {
            //1.远程调用专辑服务获取所有一级分类ID
            List<BaseCategory1> baseCategory1List = albumFeignClient.getAllCategory1().getData();
            Assert.notNull(baseCategory1List, "一级分类为空！");
            List<Long> baseCategory1IdList =
                    baseCategory1List.stream().map(BaseCategory1::getId).collect(Collectors.toList());

            //2.循环遍历所有1级分类，嵌套遍历不同排序字段得到top10
            for (Long category1Id : baseCategory1IdList) {
                //2.1 声明排序方式数组
                String[] rankingDimensionArray =
                        new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
                //2.2 遍历排序方式数组 向ES发起检索该分类下不同排序方式top10专辑记录
                for (String dimension : rankingDimensionArray) {
                    SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(
                            s -> s.index(INDEX_NAME).size(10)
                                    .query(q -> q.term(t -> t.field("category1Id").value(category1Id)))
                                    .sort(sort -> sort.field(f -> f.field(dimension).order(SortOrder.Desc))),
                            AlbumInfoIndex.class
                    );
                    //3.解析ES响应top10专辑列表 将top10专辑放入Redis-Hash中
                    //3.1 解析命中业务数据
                    List<Hit<AlbumInfoIndex>> hits = searchResponse.hits().hits();
                    if (CollectionUtil.isNotEmpty(hits)) {
                        List<AlbumInfoIndex> albumInfoIndexList =
                                hits.stream().map(hit -> hit.source()).collect(Collectors.toList());
                        //3.2 放入Redis hash
                        //3.2.1 构建排行榜hash结构key 形式： ranking：1级分类ID
                        String key = RedisConstant.RANKING_KEY_PREFIX + category1Id;
                        //3.2.2 排序字段作为field
                        String fieldKey = dimension;
                        //3.2.3 将top10专辑列表存入Reids
                        redisTemplate.opsForHash().put(key, fieldKey, albumInfoIndexList);
                    }
                }
            }
        } catch (Exception e) {
            log.error("[搜索服务]更新排行榜top10异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据分类以及排序方式获取top10专辑列表
     * @param category1Id
     * @param dimension
     * @return
     */
    @Override
    public List<AlbumInfoIndex> getRankingList(Long category1Id, String dimension) {
        //1 构建排行榜hash结构key 形式： ranking：1级分类ID
        String key = RedisConstant.RANKING_KEY_PREFIX + category1Id;
        //2 排序字段作为field
        String fieldKey = dimension;
        //3 将top10专辑列表存入Reids
        List<AlbumInfoIndex> list = (List<AlbumInfoIndex>) redisTemplate.opsForHash().get(key, fieldKey);
        return list;
    }
}
